﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using Layers;

namespace Heuristic
{
    public partial class MonteCarlo : UserControl
    {
        /* Variable */
        Pen pen;
        Pen bluePen;
        Pen redPen;
        Rectangle rect;
        Random random;
        int diameter;
        int circleRadius;

        InteractiveBuffer buffers;

        double counterPoint;
        double innerPoint;

        Thread thread;
        int sleepTime;

        byte[][] pointData;
        int arrayLength;

        System.Windows.Forms.Timer refreshTimer;


        /// <summary>
        /// Create new monte - carlo canvas
        /// </summary>
        /// <param name="width">The width of canvas, height equal the width</param>
        public MonteCarlo()
        {
            this.InitializeComponent();
            this.Load += new EventHandler(MonteCarlo_Load);
            this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            
        }

        void MonteCarlo_Load(object sender, EventArgs e)
        {
            // Time control
            sleepTime = 1000;
            refreshTimer = new System.Windows.Forms.Timer();
            refreshTimer.Interval = 200;
            refreshTimer.Tick += new EventHandler(refreshTimer_Tick);


            this.ForeColor = Color.White;
            this.BackColor = Color.White;
            counterPoint = 0;
            innerPoint = 0;

            random = new Random(DateTime.Now.Millisecond);

            // Pens

            pen = new Pen(Color.Black, 1);
            bluePen = new Pen(Color.Blue);
            redPen = new Pen(Color.Red);

            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.SupportsTransparentBackColor, true);

            // Width - 2 = 270
            rect = new Rectangle(0, 0, this.Width - 2 , this.Width - 2);
            circleRadius = rect.Width / 2;
            diameter = circleRadius * 2 ;
            // Init array
            arrayLength = this.rect.Width + 1;
            this.InitPoints();
            
            // Multilayer controller

            buffers = new InteractiveBuffer(this, rect);
            buffers.AddLayer("back"); // draw dots
            buffers.AddLayer("top"); // draw square and circle in black color
            buffers["back"].Func += new RepresetBuffer.DrawBufferHandle(DrawBack);
            buffers["top"].Func += new RepresetBuffer.DrawBufferHandle(DrawSquareAndCircle);
        }

        void refreshTimer_Tick(object sender, EventArgs e)
        {
            this.Refresh();
        }

        private void InitPoints()
        {
            pointData = new byte[arrayLength][];
            for (int i = 0; i < arrayLength; i++)
            {
                pointData[i] = new byte[arrayLength];
                for (int j = 0; j < this.arrayLength; j++)
                {
                    pointData[i][j] = 10;
                }
            }
        }

        public void DrawBack(Graphics g, Rectangle r)
        {
            g.Clear(Color.White);
            //Draw dots
            for (int i = 0; i < this.arrayLength; i++)
            {
                for (int j = 0; j < this.arrayLength; j++)
                {
                    if (pointData[i][j] == 0)
                    {
                        g.DrawRectangle(this.redPen ,i, j, 1, 1);
                        continue;
                    }
                    if (pointData[i][j] == 1)
                    {
                        g.DrawRectangle(this.bluePen, i, j, 1, 1);
                        continue;
                    }
                }
            }
        }

        public void DrawSquareAndCircle(Graphics g, Rectangle r)
        {
            g.DrawEllipse(pen, r);
            
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            this.buffers.Render(e.Graphics);
        }

        protected override void OnResize(EventArgs e)
        {
            if (buffers != null)
                buffers.RefreshContext();
        }


        #region Property
        /// <summary>
        /// Get current PI value.
        /// </summary>
        public double PI
        {
            get
            {
                return (innerPoint / counterPoint) * 4;
            }
        }

        public int Speed
        {
            get
            {
                return this.sleepTime;
            }
            set
            {
                this.sleepTime = value;
            }
        }

        /// <summary>
        /// Set calculate speed, in millisecond
        /// </summary>

        public double Points
        {
            get
            {
                return this.counterPoint;
            }
        }

        public double InnerPoints
        {
            get
            {
                return this.innerPoint;
            }
        }

        #endregion

        void CalculatePI()
        {
            while (true)
            {
                if (this.sleepTime > 0)
                    Thread.Sleep(this.sleepTime);

                // Draw points

                double dx = random.NextDouble();
                double dy = random.NextDouble();
                double dz = dx * dx + dy * dy;

                int x = (int)(-circleRadius + dx * diameter);
                int y = (int)(-circleRadius + dy * diameter);

                // Real cordinate
                int x1 = x + circleRadius;
                int y1 = circleRadius - y;

                // True computing PI
                counterPoint++;
                if(dz <= 1)
                    innerPoint++;

                // For display only
                if (Math.Sqrt(x*x + y*y) > circleRadius)
                {
                    this.pointData[x1][y1] = 0;
                }
                else
                {
                    // In circle
                    this.pointData[x1][y1] = 1;
                }
            }

        }

        public void StartCalculate()
        {
            if (thread == null)
            {
                this.counterPoint = 0;
                this.innerPoint = 0;
                thread = new Thread(new ThreadStart(this.CalculatePI));
                this.refreshTimer.Start();
                thread.Start();
            }
        }

        public void AbortCalculate()
        {
            try
            {
                thread.Abort();
                this.refreshTimer.Stop();
                thread = null;
            }
            catch (Exception exc)
            {
                exc.ToString();
            }
        }

        public void Reset()
        {
            try
            {
                if(thread != null)
                    thread.Abort();
                thread = null;
                InitPoints();
                this.StartCalculate();
            }
            catch { }

        }

    }
}
